package edu.xidian.onlinedocument.handler.excpetions;

import edu.xidian.onlinedocument.handler.excpetions.custom.CustomException;
import edu.xidian.onlinedocument.views.Result;
import edu.xidian.onlinedocument.handler.excpetions.custom.*;
import edu.xidian.onlinedocument.views.ResultCode;

import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.ServletException;
import javax.validation.Valid;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.SQLException;

/**
 * 全局的异常处理类
 * @param <T> T为要包装的类的类型
 */

@RestControllerAdvice
public class CustomExceptionHandler<T> {

    @ResponseStatus(HttpStatus.OK)
    public Result sendSuccessResponse(){
        return new Result(ResultCode.SUCCESS);
    }

    @ResponseStatus(HttpStatus.OK)
    public Result sendSuccessResponse(T data){
        return new Result(ResultCode.SUCCESS, data);
    }

    /**
     * 自定义异常类的处理
     * @param exception 异常
     * @return Result 结果集
     */
    @ExceptionHandler(CustomException.class)
    // @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)        // 500错误码，浏览器有可能收不到具体错误信息
    @ResponseStatus(HttpStatus.OK)
    public Result sendErrorResponseUserDefined(Exception exception){
        return new Result(((CustomException)exception).getException());
    }

    /**
     * http请求错误，在进入controller层之前抛出
     * @param exception 异常
     * @return Result 结果集
     */
    @ExceptionHandler(ServletException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)     // 400错误码
    public Result sendErrorResponseServlet(Exception exception) {
        //System.out.println("Message= " + exception.getMessage());
        // 400错误
        if(exception instanceof HttpClientErrorException.BadRequest){
            return new Result(ResultCode.SERVLET_REQUEST_ERROR);
        }
        // 405错误
        if(exception instanceof HttpRequestMethodNotSupportedException){
            return new Result(ResultCode.SERVLET_METHOD_NOT_ALLOWED);
        }
        // 404错误
        if(exception instanceof NoHandlerFoundException) {
            return new Result(ResultCode.SERVLET_NOT_FOUND);
        }
        return new Result(ResultCode.SERVER_ERROR);
    }

    /**
     * SQL异常处理
     * @param exception 异常
     * @return Result 结果集
     */
    @ExceptionHandler(SQLException.class)
    // @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseStatus(HttpStatus.OK)
    public Result sendErrorResponseSQL(Exception exception) {
        System.out.println("Message= " + exception.getMessage());
        SQLException sqlException = (SQLException) exception;
        /*if(sqlException.getErrorCode() == 1048){
            return new Result(ResultCode.SQL_ERROR);
        }*/
        return new Result(ResultCode.SQL_ERROR);
    }

    /**
     * sql插入异常，缺少参数或参数为Null
     * @param exception
     * @return
     */
    @ExceptionHandler(DataIntegrityViolationException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result sendErrorResponseInsertNull(Exception exception) {
        DataIntegrityViolationException dataIntegrityViolationException = (DataIntegrityViolationException) exception;
        return new Result(ResultCode.SQL_INSERT_ERROR);
    }

    /**
     * sql插入异常，插入失败，违反唯一约束
     * @param exception
     * @return
     */
    @ExceptionHandler(DuplicateKeyException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result sendErrorResponseDuplicateKey(Exception exception) {
        DuplicateKeyException duplicateKeyException = (DuplicateKeyException) exception;
        return new Result(ResultCode.SQL_INSERT_ERROR);
    }

    /**
     * valid校验拦截相关的异常处理
     * 处理{@link Valid} & {@link @NotNull}
     * @param exception 异常
     * @return Result 结果集
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    // @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseStatus(HttpStatus.OK)
    public Result sendErrorResponseValidation(Exception exception) {
        MethodArgumentNotValidException valException = (MethodArgumentNotValidException) exception;
        BindingResult res = valException.getBindingResult();
        if(res.hasFieldErrors()){
            FieldError error = res.getFieldError();
            // 尽量用String.valueOf()代替toString()，前者如果String变量为空不会报错
            System.out.println(error);
            System.out.println(error.getObjectName());
            System.out.println(error.getField());
            // objectName是类名，field是字段名
            switch(error.getObjectName()){
                // userData
                case "userData": {
                    switch(error.getField()){
                        case "userNum": {
                            if(error.getRejectedValue() == null){
                                return new Result(ResultCode.USER_ACCOUNT_IS_NULL);
                            }
                            return new Result(ResultCode.USER_ACCOUNT_FORM_ERROR);
                        }
                        case "userKey": {
                            if(error.getRejectedValue() == null){
                                return new Result(ResultCode.USER_PASSWORD_IS_NULL);
                            }
                            return new Result(ResultCode.USER_PASSWORD_FORM_ERROR);
                        }
                        case "userName": {
                            if(error.getRejectedValue() == null){
                                return new Result(ResultCode.USER_NAME_IS_NULL);
                            }
                            return new Result(ResultCode.USER_NAME_FORM_ERROR);
                        }
                        default: {
                            break;
                        }
                    }
                }break;

                // documentData
                case "documentData": {
                    switch ((error.getField())) {
                        case "docId": {
                            if(error.getRejectedValue() == null) {
                                return new Result(ResultCode.DOCUMENT_ID_IS_NULL);
                            }
                        }break;
                        case "docName": {
                            if(error.getRejectedValue() == null) {
                                return new Result(ResultCode.DOCUMENT_NAME_IS_NULL);
                            }
                        }break;
                        case "docVersion": {
                            if(error.getRejectedValue() == null) {
                                return new Result(ResultCode.DOCUMENT_VERSION_IS_NULL);
                            }
                        }break;
                        default: {
                            break;
                        }
                    }
                }break;

                // userToken
                case "userToken": {
                    switch(error.getField()){
                        case "userToken": {
                            if(error.getRejectedValue() == null){
                                return new Result(ResultCode.TOKEN_IS_NULL);
                            }
                            return new Result(ResultCode.TOKEN_FORM_ERROR);
                        }
                        default: {
                            break;
                        }
                    }
                }break;

                // Request
                case "Request": {
                    switch(error.getField()){
                        case "token": {
                            if(error.getRejectedValue() == null){
                                return new Result(ResultCode.TOKEN_IS_NULL);
                            }
                            return new Result(ResultCode.TOKEN_FORM_ERROR);
                        }
                        case "data": {
                            if(error.getRejectedValue() == null){
                                return new Result(ResultCode.DATA_IS_NULL);
                            }
                        }break;
                        default: {
                            break;
                        }
                    }
                }break;

                // 除此之外的错误
                default:{
                    break;
                }
            }
        }
        return new Result(ResultCode.USER_FAILED);
    }

    /**
     * 参数类型转换错误
     * @param exception 异常
     * @return Result 结果集
     */
    @ExceptionHandler(HttpMessageConversionException.class)
    // @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseStatus(HttpStatus.OK)
    public Result sendErrorResponseValidationType(Exception exception) {
        return new Result(ResultCode.DATA_TYPE_ERROR);
    }

    /**
     * url上的参数类型错误
     * @param exception 异常
     * @return Result 结果集
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    // @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseStatus(HttpStatus.OK)
    public Result sendErrorResponseArgumentType(Exception exception) {
        MethodArgumentTypeMismatchException methodArgumentTypeMismatchException = (MethodArgumentTypeMismatchException) exception;
        Result result = new Result(ResultCode.DATA_TYPE_ERROR);
        result.setMessage("'" + methodArgumentTypeMismatchException.getName() + "'参数类型错误");
        return result;
    }

    /**
     * 自定义的WebSocket相关错误
     * @param exception 异常
     * @return Result 结果集
     */
    @ExceptionHandler(CustomSimpleException.class)
    // @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseStatus(HttpStatus.OK)
    public Result sendErrorResponseSimpleException(Exception exception) {
        CustomSimpleException customSimpleException = (CustomSimpleException) exception;
        Result result = new Result(ResultCode.FAILED);
        result.setMessage(customSimpleException.getMessage());
        return result;
    }

    /**
     * 其他类型的异常的统一处理
     * @param exception 异常
     * @return Result 结果集
     */
    @ExceptionHandler(Exception.class)
    // @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseStatus(HttpStatus.OK)
    public Result sendErrorResponseSystem(Exception exception){
        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter = new PrintWriter(stringWriter, true);
        exception.printStackTrace(printWriter);
        stringWriter.flush();
        printWriter.flush();
        System.out.println("other exception\n" + stringWriter.toString());
        return new Result(ResultCode.SERVER_ERROR);
    }
}
